package com.ccjk.beusoft.thirdlib.plugin2.hook;

import android.content.Context;
import android.os.Handler;
import android.util.Log;

import com.ccjk.beusoft.thirdlib.plugin2.AndroidGetter;
import com.ccjk.beusoft.thirdlib.plugin2.hook.proxy.AmsProxy;
import com.ccjk.beusoft.thirdlib.plugin2.hook.proxy.HCallback;
import com.ccjk.beusoft.thirdlib.plugin2.hook.proxy.PmsProxy;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by CCJK on 2017/12/22.
 */

public class Hooker {
    private static final String TAG = "Hook";

    private Hooker() {

    }

    private static class Instance {
        private static Hooker instance = new Hooker();
    }

    public static Hooker get() {
        return Instance.instance;
    }

    private Context context;

    private static boolean isHooded = false;

    public void hookAll(Context context) {
        if (isHooded) return;
        this.context = context.getApplicationContext();
        try {
            hookAms();
            hookPms();
            hookH();
            isHooded = true;
        } catch (Exception e) {
            Log.e(TAG, "hook error", e);
        }
    }

    private void hookH() throws Exception {
        Object activityThread = AndroidGetter.get().getActivityThread();
        Field mHField = activityThread.getClass().getDeclaredField("mH");
        mHField.setAccessible(true);
        Handler mH = (Handler) mHField.get(activityThread);
        Class clazz = Class.forName("android.os.Handler$Callback");
        Field mCallbackField = clazz.getDeclaredField("mCallback");
        mCallbackField.setAccessible(true);
        mCallbackField.set(mH, new HCallback(mH));
        Log.e(TAG, "hook H success");
    }

    private void hookPms() throws Exception {
        //1.获取pms
        Class activityThreadClass = Class.forName("android.app.ActivityThread");
        Method getPackageManagerMethod = activityThreadClass.getDeclaredMethod("getPackageManager");
        getPackageManagerMethod.setAccessible(true);
        //####
        Object iPackageManager = getPackageManagerMethod.invoke(null);
        //2.设置pms代理
        Object proxy = Proxy.newProxyInstance(activityThreadClass.getClassLoader(),
                new Class[]{
                        Class.forName("android.content.pm.IPackageManager")
                }, new PmsProxy(iPackageManager));

        //3.将activityThread中sPackageManager替换为proxy
        Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
        sPackageManagerField.setAccessible(true);
        sPackageManagerField.set(null, proxy);

        //4.将ContextImpl中的包装类ApplicationPackageManager中的mPM替换为proxy
        Object wrapper = context.getPackageManager();
        Class wrapperClass = Class.forName("android.app.ApplicationPackageManager");
        Field mPMField = wrapperClass.getDeclaredField("mPM");
        mPMField.setAccessible(true);
        mPMField.set(wrapper, proxy);
        Log.e(TAG, "pms hook success");
    }

    private void hookAms() throws Exception {
        //获取ams
        Class clazz = Class.forName("android.app.ActivityManagerNative");
        Method getDefaultMethod = clazz.getDeclaredMethod("getDefault");
        getDefaultMethod.setAccessible(true);
        //#####
        Object iActivityManager = getDefaultMethod.invoke(null);
        //
        Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{
                Class.forName("android.app.IActivityManager")
        }, new AmsProxy(iActivityManager));
        //将singleton中的ams替换为proxy
        Field gDefaultField = clazz.getDeclaredField("gDefault");
        gDefaultField.setAccessible(true);
        Object singleton = gDefaultField.get(null);
        Class singletonClass = Class.forName("android.util.Singleton");
        Field mInstanceField = singletonClass.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);
        mInstanceField.set(singleton, proxy);
        Log.e(TAG, "ams hook success");
    }


}
